package org.chaos.sekiro.workorder.app.form.internal.txservice

import org.chaos.sekiro.common.ktorm.ReadOnly
import org.chaos.sekiro.workorder.app.form.domain.component.ComponentDomain
import org.chaos.sekiro.workorder.app.form.internal.assemble.assembleComponent
import org.chaos.sekiro.workorder.app.form.internal.dao.*
import org.chaos.sekiro.workorder.app.form.internal.model.Component
import org.chaos.sekiro.workorder.app.form.internal.model.ComponentBindAbility
import org.chaos.sekiro.workorder.app.form.internal.model.ComponentBindAttribute
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@ReadOnly
class ComponentTxService(
    private val componentDao: ComponentDao,
    private val componentAttributeDao: ComponentAttributeDao,
    private val componentBindAttributeDao: ComponentBindAttributeDao,
    private val componentAbilityDao: ComponentAbilityDao,
    private val componentBindAbilityDao: ComponentBindAbilityDao
) {

    fun findComponent(userId: Long, formId: Long, deleted: Boolean = false): List<ComponentDomain> {
        val components = componentDao.findByUserIdAndFormIdAndDeleted(userId, formId, deleted)
        return if (components.isEmpty()) {
            emptyList()
        } else {
            buildComponent(userId, formId, components)
        }
    }

    private fun buildComponent(userId: Long, formId: Long, components: List<Component>) : List<ComponentDomain> {
        val componentIds = components.map { it.id!! }
        val attributeBindEntities = componentBindAttributeDao.findByUserIdAndFormIdAndComponentIdIn(
            userId, formId, componentIds
        )
        val attributeIds = attributeBindEntities.map { it.attributeId }
        val attributeEntities = componentAttributeDao.findByUserIdAndIdInAndDeleted(userId, attributeIds)

        val abilityBindEntities = componentBindAbilityDao.findByUserIdAndFormIdAndComponentIdIn(
            userId, formId, componentIds
        )
        val abilityIds = abilityBindEntities.map { it.abilityId }
        val abilityEntities = componentAbilityDao.findByUserIdAndIdInAndDeleted(userId, abilityIds)

        return components.map {
            assembleComponent(it, abilityEntities, abilityBindEntities, attributeEntities, attributeBindEntities)
        }
    }

    @Transactional
    fun insertComponent(userId: Long, componentDomain: ComponentDomain): ComponentDomain {
        val component = Component {
            this.userId = userId
            formId = componentDomain.formId
            parentId = componentDomain.parentId
            name = componentDomain.name
            type = componentDomain.type
            deleted = false
        }
        componentDao.insert(listOf(component))

        val bindAttributes = componentDomain.attributes.map {
            ComponentBindAttribute {
                this.userId = userId
                formId = component.formId
                componentId = component.id!!
                attributeId = componentAttributeDao.findByUserIdAndCodeAndCustomCodeAndDeleted(
                    userId,
                    it.code,
                    it.customCode
                )!!.id!!
                value = it.value
            }
        }
        componentBindAttributeDao.insert(bindAttributes)

        val bindAbilities = componentDomain.abilities.map {
            ComponentBindAbility {
                this.userId = userId
                formId = component.formId
                componentId = component.id!!
                abilityId = componentAbilityDao.findByUserIdAndCodeAndCustomCodeAndDeleted(
                    userId,
                    it.code, it.customCode
                )!!.id!!
                value = it.value
            }
        }
        componentBindAbilityDao.insert(bindAbilities)
        return buildComponent(userId, component.formId, listOf(component)).first()
    }
}